#ifndef JCONCURRENT_H
#define JCONCURRENT_H

#include <JXQCore/JXQGlobal>
#include <JXQCore/JThreadPool>
#include <cstdio>

//#define JRUNNABLE

namespace JXQ
{

class JConcurrentPrivate;

// 并发器（线程池）
class JXQLIB_EXPORT JConcurrent
{
public:
	JConcurrent();
	static JConcurrent &globalInstance();

public:
	// 可以接受任意类型的可调用对象，并返回一个与之对应的未来类
	template <typename Func, typename... Args>
	static auto run(Func &&func, Args&&... args)
	-> std::future<decltype(func(args...))>;

	// 同上面的方法，但此方法需要传入一个回调对象
	// 且回调对象的参数必须时可调用对象的返回值类型
	template <typename CallBack, typename Func, typename... Args>
	static auto run(CallBack &&callBack, Func &&func, Args&&... args)
	-> std::future<decltype(func(args...))>;

public: //同上，下面的两个方法可以直接传入非静态类方法
	template <typename Func, class Class, typename...Args>
	static auto run(Func &&func, Class *obj, Args&&... args)
	-> std::future<decltype((obj->*func)(args...))>;

	template <typename CallBack, typename Func, class Class, typename... Args>
	static auto run(CallBack &&callBack, Func &&func, Class *obj, Args&&... args)
	-> std::future<decltype((obj->*func)(args...))>;

public:
	template <typename Func, typename... Args>
	auto operator()(Func &&func, Args&&... args)
	-> std::future<decltype(func(args...))>;

	template <typename CallBack, typename Func, typename... Args>
	auto operator()(CallBack &&callBack, Func &&func, Args&&... args)
	-> std::future<decltype(func(args...))>;

	template <typename Func, class Class, typename...Args>
	auto operator()(Func &&func, Class *obj, Args&&... args)
	-> std::future<decltype((obj->*func)(args...))>;

	template <typename CallBack, typename Func, class Class, typename... Args>
	auto operator()(CallBack &&callBack, Func &&func, Class *obj, Args&&... args)
	-> std::future<decltype((obj->*func)(args...))>;

public:
	static void setThreadPool(JThreadPool *pool);
	static JThreadPool &threadPool();

private:
	template <typename Return, typename CallBack>
	static std::future<Return>
	start(std::function<Return()> &package, CallBack &&callBack);

	template <typename CallBack>
	static std::future<void>
	start(std::function<void()> &package, CallBack &&callBack);

private:
	J_DISABLE_COPY(JConcurrent)
	JConcurrentPrivate *d_ptr;
};

template <typename Func, typename... Args>
inline auto JConcurrent::run(Func &&func, Args&&... args)
-> std::future<decltype(func(args...))>
{
	return threadPool().start(std::forward<Func>(func),
							  std::forward<Args>(args)...);
}

template <typename CallBack, typename Func, typename... Args>
inline auto JConcurrent::run(CallBack &&callBack, Func &&func, Args&&... args)
-> std::future<decltype(func(args...))>
{
	std::function<decltype(func(args...))()> package =
			std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
	return start(package, callBack);
}

template <typename Func, class Class, typename...Args>
inline auto JConcurrent::
run(Func &&func, Class *obj, Args&&... args)
-> std::future<decltype((obj->*func)(args...))>
{
	return threadPool().start(func, obj, std::forward<Args>(args)...);
}

template <typename CallBack, typename Func, class Class, typename... Args>
inline auto JConcurrent::run(CallBack &&callBack, Func &&func, Class *obj, Args&&... args)
-> std::future<decltype((obj->*func)(args...))>
{
	std::function<decltype((obj->*func)(args...))()> package =
			std::bind(func, obj, std::forward<Args>(args)...);
	return start(package, callBack);
}

template <typename Func, typename... Args>
inline auto JConcurrent::
operator()(Func &&func, Args&&... args)
-> std::future<decltype(func(args...))>
{
	return run(std::forward<Func>(func), std::forward<Args>(args)...);
}

template <typename CallBack, typename Func, typename... Args>
inline auto JConcurrent::
operator()(CallBack &&callBack, Func &&func, Args&&... args)
-> std::future<decltype(func(args...))>
{
	return run(std::forward<CallBack>(callBack), std::forward<Func>(func),
			   std::forward<Args>(args)...);
}

template <typename Func, class Class, typename...Args>
inline auto JConcurrent::
operator()(Func &&func, Class *obj, Args&&... args)
-> std::future<decltype((obj->*func)(args...))>
{
	return run(func, obj, std::forward<Args>(args)...);
}

template <typename CallBack, typename Func, class Class, typename... Args>
inline auto JConcurrent::
operator()(CallBack &&callBack, Func &&func, Class *obj, Args&&... args)
-> std::future<decltype((obj->*func)(args...))>
{
	return run(std::forward<CallBack>(callBack), func, obj,
			   std::forward<Args>(args)...);
}

template <typename Return, typename CallBack>
inline std::future<Return> JConcurrent::
start(std::function<Return()> &package, CallBack &&callBack)
{
	std::function<void(Return)> fCallBack = callBack;
	return threadPool().start([package, fCallBack]() -> Return
	{
		auto res = package();
		threadPool().start(fCallBack, res);
		return res;
	});
}

template <typename CallBack>
inline std::future<void> JConcurrent::
start(std::function<void()> &package, CallBack &&callBack)
{
	std::function<void()> fCallBack = callBack;
	return threadPool().start([package, fCallBack]() -> void
	{
		package();
		threadPool().start(fCallBack);
	});
}

} //namespace JXQ


#endif //JCONCURRENT_H
